package com.bite.friend.Service.ServiceImpl;

import Constants.RedisConstant;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bite.friend.DTO.UserExamListDTO;
import com.bite.friend.Mapper.ExamMapper;
import com.bite.friend.Mapper.UserExamMapper;
import com.bite.friend.Service.UserExamService;
import com.bite.friend.doamin.Exam;
import com.bite.friend.doamin.UserExam;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import domain.MyThreadLocal;
import domain.R;
import domain.TableDataInfo;
import enums.ResultCode;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class UserExamServiceImpl implements UserExamService {

    @Autowired
    private UserExamMapper userExamMapper;

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private StringRedisTemplate redis;
    @Override
    public R<Void> enter(String examId) {
        //判断是否存在该竞赛
        Exam exam = examMapper.selectById(examId);
        if (exam == null){
            return R.failed(ResultCode.FAILED_NOT_EXISTS);
        }
        //判断该竞赛是否已经开始了
        if (exam.getStartTime().isBefore(LocalDateTime.now())){
            return R.failed(ResultCode.FAILED_EXAM_STARTED);
        }
        //判断是否重复报名
        Long userId = Long.valueOf(MyThreadLocal.get());
        List<UserExam> userExams = userExamMapper.selectList(new LambdaQueryWrapper<UserExam>()
                .eq(UserExam::getUserId, userId)
                .eq(UserExam::getExamId, examId));
        if (userExams != null && userExams.size() > 0){
            return R.failed(ResultCode.FAILED_EXAM_ALREADY_SIGNED_UP);
        }

        //首先将数据存入到数据库中
        UserExam userExam = new UserExam();
        userExam.setUserId(userId);
        userExam.setExamId(Long.valueOf(examId));
        userExam.setCreateBy(Long.valueOf(MyThreadLocal.get()));
        userExam.setCreateTime(LocalDateTime.now());
        int insert = userExamMapper.insert(userExam);


        //接着将数据存入到redis中
        redis.opsForList().leftPush(RedisConstant.USER_EXAM_LIST+userId, examId);

        if (insert > 0){
            return R.success();
        }
        return R.failed();
    }

    @Override
    public TableDataInfo singleUserList(UserExamListDTO userExamListDTO) {
        //先从redis查看是否存在竞赛id列表
        Long userId = Long.valueOf(MyThreadLocal.get());
        Long total = redis.opsForList().size(RedisConstant.USER_EXAM_LIST + userId);
        //如果redis中不存在数据，则从数据库中获取数据，再将数据存入到redis中
        if (total == null || total <= 0){
            return getTableDataInfo(userExamListDTO, userId);
        }

        //若redis中存在数据，则直接从redis中获取数据
        int start = (userExamListDTO.getPage() - 1) * userExamListDTO.getPageSize();
        int end = start + userExamListDTO.getPageSize() - 1;
        //从redis中获取到竞赛id列表
        List<String> examIdStrList = redis.opsForList().range(RedisConstant.USER_EXAM_LIST + userId, start, end);
        if (examIdStrList == null || examIdStrList.size() == 0){
            return getTableDataInfo(userExamListDTO, userId);
        }
        //将其添加前缀
        examIdStrList = examIdStrList.stream().map(e -> RedisConstant.EXAM_DETAIL_KEY + e).collect(Collectors.toList());

        List<String> examStrList = redis.opsForValue().multiGet(examIdStrList);
        if (examStrList == null || examStrList.size() == 0){
            return getTableDataInfo(userExamListDTO, userId);
        }
        List<Exam> examList = examStrList.stream().map(e -> JSONUtil.toBean(e, Exam.class)).toList();
        //如果数量不相同，说明redis中的数据有误，则需要刷新redis
        if (examList.size() != examIdStrList.size()){
            return getTableDataInfo(userExamListDTO, userId);
        }

        return TableDataInfo.success(examList,total);
    }


    private TableDataInfo getTableDataInfo(UserExamListDTO userExamListDTO, Long userId) {
        PageHelper.startPage(userExamListDTO.getPage(), userExamListDTO.getPageSize());
        List<Exam> examList = userExamMapper.selectSingleUserExamList(userId);
        if (examList == null || examList.size() == 0){
            return TableDataInfo.empty();
        }
        //将数据刷新到redis中
        refreshRedis(userId, examList);
        //返回数据
        Page page = (Page) examList;
        return TableDataInfo.success(page.getResult(), page.getTotal());
    }

    public void refreshRedis(Long userId, List<Exam> examList) {
        if (examList == null || examList.size() == 0){
            return;
        }
        //将数据刷新到redis中
        List<String> examIdList = examList.stream().map(e -> e.getExamId().toString()).toList();

        //先清空之前的列表数据
        redis.delete(RedisConstant.USER_EXAM_LIST+userId);
        //再将数据存入到redis中
        redis.opsForList().leftPushAll(RedisConstant.USER_EXAM_LIST+userId,examIdList);

        Map<String, String> collect = examList.stream()
                .collect(Collectors.toMap(e -> RedisConstant.EXAM_DETAIL_KEY + e.getExamId(), JSONUtil::toJsonStr));
        redis.opsForValue().multiSet(collect);
    }
}
